/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "kh_sle_host.h"
#include "kh_sle_seek.h"
#include "sle_seek_hal.h"
#include "sle_seek_config.h"
#include "sle_hilog.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <cmsis_os2.h>
#include "sle_lite_advance_setting.h"

#ifndef ServiceCallbackNumFlag
#define ServiceCallbackNumFlag
#define serviceCallbackNum  3
#endif  // ServiceCallbackNumFlag
static bool g_isSeekServiceInit = false;
static SleSeekState g_sleSeekState = SLE_SEEK_ERROR_STATE;
static SleSeekCallbacks* g_seekCbs[serviceCallbackNum] = {NULL};
static SleSeekHalCallbacks* g_seekHalCallback = NULL;
static osMutexId_t g_seekCallbcakMuxHandle = 0;
static osSemaphoreId_t g_sleSeekSemHandle = 0;
static uint32_t SleSeekTimeOut = 300;
static SleErrorCode g_sleSeekAsyncRet = SLE_ERR_UNKNOWN;
// TODO: SleSeekResult g_seekResult[20] = {0};  static uint32_t *g_seekStaticParamMuxHandle = NULL;

void NotifySleSeekStateChange(SleSeekState seekState)
{
    SLE_LOGD("NotifySleSeekStateChange In");
    osMutexAcquire(g_seekCallbcakMuxHandle, 0);
    g_sleSeekState = seekState;
    for (uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_seekCbs[i] != NULL && g_seekCbs[i]->OnSleSeekStateChangeCb != NULL) {
            g_seekCbs[i]->OnSleSeekStateChangeCb(g_sleSeekState);
        }
    }
}

void OnSleSeekHalEnableSeek(SleHalErrorCode errCode)
{
    SLE_LOGD("OnSleSeekHalEnableSeek In errCode(%d)", errCode);
    g_sleSeekAsyncRet = (SleErrorCode)errCode;
    osSemaphoreRelease(g_sleSeekSemHandle);
    if(errCode == SLE_HAL_SUCCESS) {
        NotifySleSeekStateChange(SLE_SEEK_ENABLED_STATE);
    } else {
        g_sleSeekState = SLE_SEEK_DISABLED_STATE;
    }
}

void OnSleSeekHalDisableSeek(SleHalErrorCode errCode)
{
    SLE_LOGD("OnSleSeekHalDisableSeek In errCode(%d)", errCode);
    g_sleSeekAsyncRet = (SleErrorCode)errCode;
    osSemaphoreRelease(g_sleSeekSemHandle);
    if(errCode == SLE_HAL_SUCCESS) {
        NotifySleSeekStateChange(SLE_SEEK_DISABLED_STATE);
    } else {
        g_sleSeekState = SLE_SEEK_ENABLED_STATE;
    }
}

void OnSleSeekHalSeekResult(const SleSeekResult *result)
{
#if 0
    SLE_LOGD("OnSleSeekHalSeekResult In");
#endif
    // TODO:过滤已在扫描列表中的设备
    osMutexAcquire(g_seekCallbcakMuxHandle, 0);
    SleSeekResult resultCb;
    memcpy(&resultCb, result, sizeof(SleSeekResult));
    for (uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_seekCbs[i] != NULL && g_seekCbs[i]->OnSleSeekResultCb != NULL) {
            g_seekCbs[i]->OnSleSeekResultCb(&resultCb);
        }
    }
    osMutexRelease(g_seekCallbcakMuxHandle);
}

SleErrorCode InitSleSeekService(void)
{
    SLE_LOGD("InitSleSeekService In");
    SLE_ASSERT_AND_RETURN_RET(g_isSeekServiceInit == false, SLE_NO_ERROR);
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    ret = InitSleSeekHal();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    if (g_seekCallbcakMuxHandle == 0) {
      osMutexAttr_t attr = {
            .name = "sle_seek_mux",
        };
        g_seekCallbcakMuxHandle = osMutexNew(&attr);
    }

    if (g_sleSeekSemHandle == 0) {
        // osStatus_t uwRet = LOS_BinarySemCreate(1, &g_sleSeekSemHandle);
        g_sleSeekSemHandle = osSemaphoreNew(1, 1, NULL);
        // SLE_ASSERT_AND_RETURN_RET(uwRet == osOK, SLE_ERR_SYSTEM_WRONG);
    }

    if(g_seekHalCallback == NULL) {
        g_seekHalCallback = (SleSeekHalCallbacks*)malloc(sizeof(SleSeekHalCallbacks));
        SLE_ASSERT_AND_RETURN_RET(g_seekHalCallback != NULL, SLE_ERR_SYSTEM_WRONG);
        g_seekHalCallback->OnSeekHalEnableSeekCb = OnSleSeekHalEnableSeek;
        g_seekHalCallback->OnSeekHalDisableSeekCb = OnSleSeekHalDisableSeek;
        g_seekHalCallback->OnSeekHalSeekResultCb = OnSleSeekHalSeekResult;
    }

    ret = (SleErrorCode) SleSeekHalRegisterCallback(g_seekHalCallback);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    g_sleSeekAsyncRet = SLE_ERR_UNKNOWN;
    g_isSeekServiceInit = true;
    g_sleSeekState = SLE_SEEK_DISABLED_STATE;
    return ret;
}

SleErrorCode CheckCanOprateSeek(void)
{
    SLE_ASSERT_AND_RETURN_RET(g_isSeekServiceInit == true, SLE_ERR_NOT_READY);
    SleDeviceState hostState;
    SleErrorCode ret = GetSleState(&hostState);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    SLE_ASSERT_AND_RETURN_RET(hostState == SLE_HOST_STATE_ENABLED, SLE_ERR_STATUS_WRONG);
    return SLE_NO_ERROR;
}

SleErrorCode DeinitSleSeekService(void)
{
    SLE_LOGD("DeinitSleSeekService In");
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    ret = CheckCanOprateSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    ret = DeinitSleSeekHal();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    osMutexAcquire(g_seekCallbcakMuxHandle, 0);
    if (g_seekCallbcakMuxHandle == 0) {
        SLE_LOGW("g_seekCallbcakMuxHandle is 0");
        return SLE_NO_ERROR;
    }
    osMutexRelease(g_seekCallbcakMuxHandle);
    osMutexDelete(g_seekCallbcakMuxHandle);
    g_seekCallbcakMuxHandle = 0;
    osSemaphoreDelete(g_sleSeekSemHandle);
    g_sleSeekSemHandle = 0;
    g_sleSeekAsyncRet = SLE_ERR_UNKNOWN;
    g_isSeekServiceInit = false;
    g_sleSeekState = SLE_SEEK_ERROR_STATE;
    return ret;
}

static bool CheckSeekParamArray(const uint8_t type[], const uint16_t interval[], const uint16_t window[])
{
    bool allZero = true;
    for (int i = 0; i < SLE_SEEK_PHY_MAX; ++i) {
        if (type[i] != 0 || interval[i] != 0 || window[i] != 0) {
            allZero = false;
        }
    }
    if (allZero) { // 参数全0
        return false;
    }
    // 参数非全0
    bool zero[SLE_SEEK_PHY_MAX] = {false};
    for (int i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        if (type[i] != SLE_PASSIVE_SEEK && type[i] != SLE_ACTIVE_SEEK) { // 扫描type
            return false;
        }
        if (interval[i] != 0 && window[i] != 0) { // 同脚标扫描间隔和窗口全不为0
            if (interval[i] < 0x0004 || interval[i] > 0xFFFF) { // 扫描间隔
                return false;
            }
            if (window[i] < 0x0004 || window[i] > 0xFFFF) { // 扫描窗口
                return false;
            }
            for (int j = 0; j < SLE_SEEK_PHY_MAX; j++) { // 扫描窗口不大于扫描间隔
                if (window[j] > interval[j]) {
                    return false;
                }
            }
            zero[i] = true;
        } else if (type[i] == 0 && interval[i] == 0 && window[i] == 0) { // 同脚标全为0
            zero[i] = true;
        }
    }
    for (int i = 0; i < SLE_SEEK_PHY_MAX; i++) { // 扫描间隔或扫描窗口为0
        if (zero[i] == false) {
            return false;
        }
    }
    return true;
}

static SleErrorCode CheckSeekParam(const SleSeekParam *param)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(param->addrType == SLE_PUBLIC_ADDRESS_TYPE || param->addrType == SLE_RANDOM_ADDRESS_TYPE,
        SLE_ERR_INVALID_PARAM, "param seek device type error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(param->filterPolicy == SLE_SEEK_FILTER_ALL_ALLOW || param->filterPolicy == SLE_SEEK_FILTER_WLST_ALLOW,
        SLE_ERR_INVALID_PARAM, "param seek filter policy error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(param->phys == SLE_SEEK_1M_PHY || param->phys == SLE_SEEK_2M_PHY || param->phys == SLE_SEEK_4M_PHY,
        SLE_ERR_INVALID_PARAM, "param seek phys error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(CheckSeekParamArray(param->type, param->interval, param->window),
        SLE_ERR_INVALID_PARAM, "param seek phys error!");
    return SLE_NO_ERROR;
}

SleErrorCode SetSeekParam(const SleSeekParam *param)
{
    SLE_LOGD("SetSeekParam In");
    SleSeekParam defaultParam = {
        .addrType = SLE_PUBLIC_ADDRESS_TYPE,
        .repateFilter = DEFAULT_SLE_SEEK_REPATE_FILTER,
        .filterPolicy = DEFAULT_SLE_SEEK_FILTER_POLICY,
        .phys = DEFAULT_SLE_SEEK_PHYS,
        .type[0] = DEFAULT_SLE_SEEK_TYPE,
        .interval[0] = DEFAULT_SLE_SEEK_INTERVAL,
        .window[0] = DEFAULT_SLE_SEEK_INTERVAL,
    };
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    if(param != NULL) {
        ret = CheckSeekParam(param);
        SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
        memcpy(&defaultParam, param, sizeof(SleSeekParam));
    }
    ret = SleSeekHalSetSeekParam(&defaultParam);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return ret;
}

SleErrorCode StartSeekAsync(const SleSeekParam *param)
{
    SLE_LOGD("StartSeekAsync In");
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    ret = CheckCanOprateSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    SLE_ASSERT_AND_RETURN_RET(g_sleSeekState != SLE_SEEK_ENABLED_STATE, SLE_ERR_EBABLE_SEEK_SUCCESS_WHEN_SEEK);
    SLE_ASSERT_AND_RETURN_RET(g_sleSeekState == SLE_SEEK_DISABLED_STATE, SLE_ERR_STATUS_WRONG);
    ret = SetSeekParam(param);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    ret = SleSeekHalStartSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    g_sleSeekState = SLE_SEEK_ENABLING_STATE;
    return ret;
}

SleErrorCode StartSeek(const SleSeekParam *param)
{
    SLE_LOGD("StartSeek In");
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    ret = CheckCanOprateSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    SLE_ASSERT_AND_RETURN_RET(g_sleSeekState != SLE_SEEK_ENABLED_STATE, SLE_ERR_EBABLE_SEEK_SUCCESS_WHEN_SEEK);
    SLE_ASSERT_AND_RETURN_RET(g_sleSeekState == SLE_SEEK_DISABLED_STATE, SLE_ERR_STATUS_WRONG);
    ret = SetSeekParam(param);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    g_sleSeekState = SLE_SEEK_ENABLING_STATE;
    ret = SleSeekHalStartSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    osStatus_t uwRet = osSemaphoreAcquire(g_sleSeekSemHandle, SleSeekTimeOut);
    SLE_ASSERT_AND_RETURN_RET(uwRet != osErrorTimeout, SLE_ERR_TIME_OUT);
    ret = g_sleSeekAsyncRet;
    g_sleSeekAsyncRet = SLE_ERR_UNKNOWN;
    return ret;
}

SleErrorCode StopSeek(void)
{
    SLE_LOGD("StopSeek In");
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    ret = CheckCanOprateSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    SLE_ASSERT_AND_RETURN_RET(g_sleSeekState != SLE_SEEK_DISABLED_STATE, SLE_ERR_DISABLE_SEEK_SUCCESS_WHEN_NOT_SEEK);
    SLE_ASSERT_AND_RETURN_RET(g_sleSeekState == SLE_SEEK_ENABLED_STATE, SLE_ERR_STATUS_WRONG);
    g_sleSeekState = SLE_SEEK_DISABLING_STATE;
    ret = SleSeekHalStopSeek();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    osStatus_t uwRet = osSemaphoreAcquire(g_sleSeekSemHandle, SleSeekTimeOut);
    SLE_ASSERT_AND_RETURN_RET(uwRet != osErrorTimeout, SLE_ERR_TIME_OUT);
    ret = g_sleSeekAsyncRet;
    g_sleSeekAsyncRet = SLE_ERR_UNKNOWN;
    return ret;
}

SleErrorCode SleConvertByteToSleSeekConfig(uint8_t *data, SleSeekParam *seekConnParam)
{
    SLE_ASSERT_AND_RETURN_RET(data != NULL, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(seekConnParam != NULL, SLE_ERR_INVALID_PARAM);

    uint8_t pos = 0;
    seekConnParam->addrType = *(data + pos);
    pos += sizeof(uint8_t);
    seekConnParam->repateFilter = *(data + pos);
    pos += sizeof(bool);
    seekConnParam->filterPolicy = *(data + pos);
    pos += sizeof(uint8_t);
    seekConnParam->phys = *(data + pos);
    pos += sizeof(uint8_t);

    for(uint8_t i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        seekConnParam->type[i] = *(data + pos);
        pos += sizeof(uint8_t);
    }

    for(uint8_t i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        seekConnParam->interval[i] = COMBINE_BYTS_16(*(data + pos), *(data + pos + 1));
        pos += sizeof(uint16_t);
    }

    for(uint8_t i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        seekConnParam->window[i] = COMBINE_BYTS_16(*(data + pos), *(data + pos + 1));
        pos += sizeof(uint16_t);
    }

    return SLE_NO_ERROR;
}

SleErrorCode GetDefaultSeekParamConfig(SleSeekParam *seekConfig)
{
    SLE_LOGD("GetSeekDefaultConnectConfig In");
    SLE_ASSERT_AND_RETURN_RET(seekConfig != NULL, SLE_ERR_INVALID_PARAM);

    uint16_t realLen = 0;
    uint8_t data[SLE_NV_SEEK_CONFIG_DATA_LEN] = {0};
    SleErrorCode ret = SleUapiNvRead(SLE_NV_SEEK_ID, SLE_NV_SEEK_CONFIG_DATA_LEN, &realLen, data);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    return SleConvertByteToSleSeekConfig(data, seekConfig);
}

static bool CheckIsRegistered(const SleSeekCallbacks *seekCallbacks)
{
    for(uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_seekCbs[i] == seekCallbacks) {
            return true;
        }
    }
    return false;
}

SleErrorCode RegisterSeekCallbacks(SleSeekCallbacks *seekCallbacks)
{
    SLE_LOGD("RegisterSeekCallbacks In");
    SLE_ASSERT_AND_RETURN_RET(g_isSeekServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(seekCallbacks != NULL, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET_LOG(CheckIsRegistered(seekCallbacks) != true, SLE_NO_ERROR, "seekCallbacks is already registered");
    SleErrorCode ret = SLE_ERR_FAILED;
    uint8_t count = 0;
    osMutexAcquire(g_seekCallbcakMuxHandle, 0);
    for(uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_seekCbs[i] == NULL) {
            g_seekCbs[i] = seekCallbacks;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    osMutexRelease(g_seekCallbcakMuxHandle);
    SLE_ASSERT_AND_RETURN_RET(count < serviceCallbackNum, SLE_ERR_NO_MEM);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return ret;
}

SleErrorCode UnregisterSeekCallbacks(SleSeekCallbacks *seekCallbacks)
{
    SLE_LOGD("UnregisterSeekCallbacks In");
    SLE_ASSERT_AND_RETURN_RET(g_isSeekServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(seekCallbacks != NULL, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(CheckIsRegistered(seekCallbacks) == true, SLE_NO_ERROR);
    SleErrorCode ret = SLE_ERR_FAILED;
    osMutexAcquire(g_seekCallbcakMuxHandle, 0);
    for(uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_seekCbs[i] == seekCallbacks) {
            g_seekCbs[i] = NULL;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    osMutexRelease(g_seekCallbcakMuxHandle);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return ret;
}
